package com.senlingspace.common.utils;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

/**
 * token 生成及验证
 *
 * @author lebronjames
 */
@Slf4j
public class JwtUtilExt {

    // 用于加密
    private static final String SECRET_KEY = "x0eDXE9DZ6NXWjzNe9527S60rL36ffOrRUGbPRR4BsnZVetxbW3PdG666IK4xjiq3XQ4";
    private static final JWTSigner JWT_SIGNER = JWTSignerUtil.hs256(SECRET_KEY.getBytes(StandardCharsets.UTF_8));

    // 有效期是几天
    public static final int EXPIRE_DAY = 15;

    // token前缀
    public static final String TOKEN_PREFIX = "Bearer ";

    private JwtUtilExt() {
    }

    /**
     * 生成token
     *
     * @param payload
     * @param expire  过期时间
     * @return
     */
    public static String createToken(final Map<String, Object> payload, final long expire) {
        String token = JWT.create()
                .setCharset(StandardCharsets.UTF_8)
                .addPayloads(payload)
                .setIssuedAt(new Date())
                .setExpiresAt(new Date(expire))
                .sign(JWT_SIGNER);
        return TOKEN_PREFIX.concat(token);
    }

    /**
     * 验证token
     *
     * @param token
     * @return
     */
    public static boolean validateToken(String token) {
        if (StringUtils.isBlank(token)) {
            log.info("token为空！");
            return false;
        }
        if (token.startsWith(TOKEN_PREFIX)) {
            token = token.replaceFirst(TOKEN_PREFIX, StringUtils.EMPTY);
        }
        JWT jwt = JWT.of(token).setSigner(JWT_SIGNER);
        if (BooleanUtil.isFalse(jwt.verify())) {
            log.info("无效token：{}", token);
            return false;
        }
        if (BooleanUtil.isFalse(jwt.validate(30L))) {
            log.info("过期token：{}", token);
            return false;
        }
        return true;
    }

    /**
     * 是否过期
     *
     * @param token
     * @return
     */
    public static boolean isExpire(String token) {
        if (token.startsWith(TOKEN_PREFIX)) {
            token = token.replaceFirst(TOKEN_PREFIX, StringUtils.EMPTY);
        }
        return JWT.of(token).setSigner(JWT_SIGNER).validate(30L);
    }

    /**
     * 是否合规
     *
     * @param token
     * @return
     */
    public static boolean isValidity(String token) {
        if (token.startsWith(TOKEN_PREFIX)) {
            token = token.replaceFirst(TOKEN_PREFIX, StringUtils.EMPTY);
        }
        return JWT.of(token).setSigner(JWT_SIGNER).verify();
    }

    /**
     * 获取用户ID
     *
     * @param token
     * @return
     */
    public static Long getCustomerId(String token) {
        if (token.startsWith(TOKEN_PREFIX)) {
            token = token.replaceFirst(TOKEN_PREFIX, StringUtils.EMPTY);
        }
        Payload payload = parseToken(token);
        if (Objects.isNull(payload)) {
            return null;
        }
        return payload.getCustomerId();
    }

    /**
     * 获取用户电话
     *
     * @param token
     * @return
     */
    public static String getPhone(String token) {
        if (token.startsWith(TOKEN_PREFIX)) {
            token = token.replaceFirst(TOKEN_PREFIX, StringUtils.EMPTY);
        }
        Payload payload = parseToken(token);
        if (Objects.isNull(payload)) {
            return "";
        }
        return payload.getPhone();
    }

    /**
     * token解析
     *
     * @param token
     * @return
     */
    public static Payload parseToken(String token) {
        if (token.startsWith(TOKEN_PREFIX)) {
            token = token.replaceFirst(TOKEN_PREFIX, StringUtils.EMPTY);
        }
        Payload data = new Payload();
        try {
            JWTPayload payload = JWT.of(token).setSigner(JWT_SIGNER).getPayload();
            data.setCustomerId(NumberUtils.toLong(payload.getClaim("customer_id") + "", -1));
            data.setPhone(payload.getClaim("phone") + "");
            data.setNickname(payload.getClaim("nickname") + "");

            ZoneId zoneId = ZoneId.of("Asia/Shanghai");
            Instant expInstant = Instant.ofEpochSecond(NumberUtils.toLong(payload.getClaim("exp") + ""));
            data.setExpire(LocalDateTime.ofInstant(expInstant, zoneId));
            Instant iatInstant = Instant.ofEpochSecond(NumberUtils.toLong(payload.getClaim("iat") + ""));
            data.setIat(LocalDateTime.ofInstant(iatInstant, zoneId));
        } catch (Exception e) {
            log.error("Token解析异常：{}", token, e);
            return null;
        }
        return data;
    }

    @Data
    public static class Payload implements Serializable {
        /**
         * 用户ID
         * <p>
         * 如果是 -1，说明没有ID
         */
        private Long customerId;

        /**
         * 用户电话
         */
        private String phone;

        /**
         * 用户昵称
         */
        private String nickname;

        /**
         * 过期时间
         */
        private LocalDateTime expire;

        /**
         * JWT的签发时间
         */
        private LocalDateTime iat;
    }

}
